#pragma once


#ifndef __CANVAS_COLLECTION_DICTIONARY_H
#define __CANVAS_COLLECTION_DICTIONARY_H

#include "Interface.h"
#include "Pair.h"

namespace Canvas 
{
	namespace Collection
	{
		template<
			class KT,
			class VT,
			class KK = typename KeyType<KT>::Type,
			class VK = typename KeyType<VT>::Type>
		class Dictionary : public Object, public virtual IEnumerable<Pair<KT, VT>>
		{
		public:
			typedef SortedList<KT, KK> KeyContainer;
			typedef List<VT, VK> ValueContainer;

		protected:
			class Enumerator : public Object, public virtual IEnumerator<Pair<KT, VT>>
			{
			private:
				const Dictionary<KT, VT, KK, VK>* container;
				int index;
				Pair<KT, VT> current;

				void UpdateCurrent()
				{
					if (index < container->Count())
					{
						current.key = container->Keys().Get(index);
						current.value = container->Values().Get(index);
					}
				}
			public:
				Enumerator(const Dictionary<KT, VT, KK, VK>* _container, int _index = 0)
					: container(_container), index(_index) {}

				IEnumerator<Pair<KT, VT>>* Clone() const
				{
					return new Enumerator(container, index);
				}

				const Pair<KT, VT>& Current() const
				{
					return current;
				}

				int Index() const
				{
					return index;
				}

				bool Next()
				{
					UpdateCurrent();
					return (index++ < container->Count());
				}

				void Reset()
				{
					index = 0;
					UpdateCurrent();
				}
			};

			KeyContainer keys;
			ValueContainer values;
		public:
			Dictionary() {}
			IEnumerator<Pair<KT, VT>>* CreateEnumerator() const
			{
				return new Enumerator(this);
			}

			void SetLessMemoryMode(bool mode)
			{
				keys.SetLessMemoryMode(mode);
				values.SetLessMemoryMode(mode);
			}

			const KeyContainer& Keys() const
			{
				return keys;
			}

			const ValueContainer& Values() const
			{
				return values;
			}

			int Count() const
			{
				return keys.Count();
			}

			const VT& Get(const KK& key) const
			{
				return values.Get(keys.IndexOf(key));
			}

			const VT& operator[](const KK& key) const
			{
				return values.Get(keys.IndexOf(key));
			}

			bool Set(const KT& key, const VT& value)
			{
				auto index = keys.IndexOf(KeyType<KT>::GetKeyValue(key));
				if (index >= keys.Count() || index < 0)
				{
					index = keys.Add(key);
					values.Insert(index, value);
				}
				else
				{
					values[index] = value;
				}
				return true;
			}

			bool Add(const Pair<KT, VT>& value)
			{
				return Add(value.key, value.value);
			}

			bool Add(const KT& key, const VT& value)
			{
				if (keys.Contains(KeyType<KT>::GetKeyValue(key))) {
					throw ReferenceError();
				}
				auto index = keys.IndexOf(key);
				values.Insert(index, value);
				return true;
			}

			bool Remove(const KK& key)
			{
				auto index = keys.IndexOf(key);
				if (index < keys.Count())
				{
					keys.Remove(index);
					values.RemoveAt(index);
					return true;
				}
				return false;
			}

			bool Clear()
			{
				keys.Clear();
				values.Clear();
				return true;
			}
		};

		template<
			typename KT,
			typename VT,
			typename KK = typename KeyType<KT>::Type,
			typename VK = typename KeyType<VT>::Type
		>
		class Group : public Object, public virtual IEnumerable<Pair<KT, VT>>
		{
			typedef SortedList<KT, KK> KeyContainer;
			typedef List<VT, VK> ValueContainer;
		protected:
			class Enumerator : public Object, public virtual IEnumerator<Pair<KT, VT>>
			{
			private:
				const Group<KT, VT, KK, VK>* container;
				int keyIndex;
				int valueIndex;
				Pair<KT, VT> current;

				void UpdateCurrent()
				{
					if (keyIndex<container->Count())
					{
						const ValueContainer& values = container->GetByIndex(keyIndex);
						if (valueIndex<values.Count())
						{
							current.key = container->Keys().Get(keyIndex);
							current.value = values.Get(valueIndex);
						}
					}
				}
			public:
				Enumerator(const Group<KT, VT, KK, VK>* _container, int _keyIndex = -1, int _valueIndex = -1)
				{
					container = _container;
					keyIndex = _keyIndex;
					valueIndex = _valueIndex;
				}

				IEnumerator<Pair<KT, VT>>* Clone()const
				{
					return new Enumerator(container, keyIndex, valueIndex);
				}

				const Pair<KT, VT>& Current() const
				{
					return current;
				}

				int Index() const
				{
					if (0 <= keyIndex && keyIndex<container->Count())
					{
						int index = 0;
						for (int i = 0; i<keyIndex; i++)
						{
							index += container->GetByIndex(i).Count();
						}
						return index + valueIndex;
					}
					else
					{
						return -1;
					}
				}

				bool Next()
				{
					if (keyIndex == -1)
					{
						keyIndex = 0;
					}
					while (keyIndex<container->Count())
					{
						valueIndex++;
						const ValueContainer& values = container->GetByIndex(keyIndex);
						if (valueIndex<values.Count())
						{
							UpdateCurrent();
							return true;
						}
						else
						{
							keyIndex++;
							valueIndex = -1;
						}
					}
					return false;
				}

				void Reset()
				{
					keyIndex = -1;
					valueIndex = -1;
					UpdateCurrent();
				}
			};

			KeyContainer keys;
			List<ValueContainer*> values;
		public:
			Group()
			{
			}

			~Group()
			{
				Clear();
			}

			IEnumerator<Pair<KT, VT>>* CreateEnumerator() const
			{
				return new Enumerator(this);
			}

			const KeyContainer& Keys() const
			{
				return keys;
			}

			int Count() const
			{
				return keys.Count();
			}

			const ValueContainer& Get(const KK& key) const
			{
				return *values.Get(keys.IndexOf(key));
			}

			const ValueContainer& GetByIndex(int index) const
			{
				return *values.Get(index);
			}

			const ValueContainer& operator[](const KK& key) const
			{
				return *values.Get(keys.IndexOf(key));
			}

			bool Contains(const KK& key) const
			{
				return keys.Contains(key);
			}

			bool Contains(const KK& key, const VK& value) const
			{
				int index = keys.IndexOf(key);
				if (index != -1)
				{
					return values.Get(index)->Contains(value);
				}
				else
				{
					return false;
				}
			}

			bool Add(const Pair<KT, VT>& value)
			{
				return Add(value.key, value.value);
			}

			bool Add(const KT& key, const VT& value)
			{
				ValueContainer* target = 0;
				int index = keys.IndexOf(KeyType<KT>::GetKeyValue(key));
				if (index == -1)
				{
					target = new ValueContainer;
					values.Insert(keys.Add(key), target);
				}
				else
				{
					target = values[index];
				}
				target->Add(value);
				return true;
			}

			bool Remove(const KK& key)
			{
				int index = keys.IndexOf(key);
				if (index != -1)
				{
					keys.RemoveAt(index);
					List<VT, VK>* target = values[index];
					values.RemoveAt(index);
					delete target;
					return true;
				}
				else
				{
					return false;
				}
			}

			bool Remove(const KK& key, const VK& value)
			{
				int index = keys.IndexOf(key);
				if (index != -1)
				{
					List<VT, VK>* target = values[index];
					target->Remove(value);
					if (target->Count() == 0)
					{
						keys.RemoveAt(index);
						values.RemoveAt(index);
						delete target;
					}
					return true;
				}
				else
				{
					return false;
				}
			}

			bool Clear()
			{
				for (int i = 0; i<values.Count(); i++)
				{
					delete values[i];
				}
				keys.Clear();
				values.Clear();
				return true;
			}
		};

		template<
			typename TKey,
			typename TValueFirst,
			typename TValueSecond,
			typename TDiscardFirst,		// TKey * [TValueFirst] -> void
			typename TDiscardSecond,	// TKey * [TValueSecond] -> void
			typename TAccept			// TKey * [TValueFirst] * [TValueSecond] -> void
		>
		void GroupInnerJoin(
		const Group<TKey, TValueFirst>& first,
		const Group<TKey, TValueSecond>& second,
		const TDiscardFirst& discardFirst,
		const TDiscardSecond& discardSecond,
		const TAccept& accept
		)
		{
			int firstIndex = 0;
			int secondIndex = 0;
			int firstCount = first.Keys().Count();
			int secondCount = second.Keys().Count();
			while (true)
			{
				if (firstIndex < firstCount)
				{
					WString firstKey = first.Keys()[firstIndex];
					const List<TValueFirst>& firstValues = first.GetByIndex(firstIndex);

					if (secondIndex < secondCount)
					{
						WString secondKey = second.Keys()[secondIndex];
						const List<TValueSecond>& secondValues = second.GetByIndex(secondIndex);

						if (firstKey < secondKey)
						{
							discardFirst(firstKey, firstValues);
							firstIndex++;
						}
						else if (firstKey > secondKey)
						{
							discardSecond(secondKey, secondValues);
							secondIndex++;
						}
						else
						{
							accept(firstKey, firstValues, secondValues);
							firstIndex++;
							secondIndex++;
						}
					}
					else
					{
						discardFirst(firstKey, firstValues);
						firstIndex++;
					}
				}
				else
				{
					if (secondIndex < secondCount)
					{
						WString secondKey = second.Keys()[secondIndex];
						const List<TValueSecond>& secondValues = second.GetByIndex(secondIndex);

						discardSecond(secondKey, secondValues);
						secondIndex++;
					}
					else
					{
						break;
					}
				}
			}
		}

		namespace RandomAccessInternal
		{
			template<typename KT, typename VT, typename KK, typename VK>
			struct RandomAccessable<Dictionary<KT, VT, KK, VK>>
			{
				static const bool							CanRead = true;
				static const bool							CanResize = false;
			};

			template<typename KT, typename VT, typename KK, typename VK>
			struct RandomAccess<Dictionary<KT, VT, KK, VK>>
			{
				static int GetCount(const Dictionary<KT, VT, KK, VK>& t)
				{
					return t.Count();
				}

				static Pair<KT, VT> GetValue(const Dictionary<KT, VT, KK, VK>& t, int index)
				{
					return Pair<KT, VT>(t.Keys().Get(index), t.Values().Get(index));
				}

				static void AppendValue(Dictionary<KT, VT, KK, VK>& t, const Pair<KT, VT>& value)
				{
					t.Set(value.key, value.value);
				}
			};
		}
	}
}

#endif